//Transformations-based sprite class
#include "ui_trsprite.h"

#include "graphics/image.h"
#include "graphics/graphics.h"
#include "graphics/graphics_util.h"
#include "util/timer.h"				// Needs to come in early
#include "util/mmgr.h"				// Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"				// logging

#include "ui_manager.h"

#include "math/math_core.h"

#include <cassert>
#include <math.h>

//TrSprite: Constructor and destructor
//======================================================================================================================

TrSprite::TrSprite() :
	m_IsVisible(true),
	m_UV1(0.0f, 0.0f),
	m_UV2(1.0f, 1.0f),
	m_pImage(NULL),
	m_ImageSize(0.0f, 0.0f),
	m_IsAdditive(false),
	m_IsMipMapped(false),
	m_QuickAlpha(false), 
	m_pSpriteInstance(NULL),
	m_Timer(0),
	m_Paused(false),
	m_Looped(true),
	m_LoopCount(0),
	m_IsLocalized(false),
	m_Filename("")
{
	for (int i = 0; i < 4; i++)
		m_Color[i] = 0xFFFFFFFF;

}//TrSprite::TrSprite()



TrSprite::~TrSprite()
{
	Invalidate();
}//TrSprite::~TrSprite()







//TrSprite: Main Routines
//======================================================================================================================

bool TrSprite::Render()
{

	if (m_IsVisible == false || IsValid() == false)
		return false;

	Graphics* pG = GetGraphics();

	float	ScreenWidth = pG->GetScreenWidth();
	float	ScreenHeight = pG->GetScreenHeight();

	Graphics::Vertex2D*		VArray = pG->GetTempVert();

	//filling the vertices array
	VArray[0].Fill((m_Quad[3].x*ScreenWidth)-0.5f, (m_Quad[3].y*ScreenHeight)-0.5f, 0.0f, 0.5f, m_Color[3], m_UV1.x, m_UV2.y);
	VArray[1].Fill((m_Quad[0].x*ScreenWidth)-0.5f, (m_Quad[0].y*ScreenHeight)-0.5f, 0.0f, 0.5f, m_Color[0], m_UV1.x, m_UV1.y);
	VArray[2].Fill((m_Quad[1].x*ScreenWidth)-0.5f, (m_Quad[1].y*ScreenHeight)-0.5f, 0.0f, 0.5f, m_Color[1], m_UV2.x, m_UV1.y);
	
	VArray[3]=VArray[2];
	VArray[4].Fill((m_Quad[2].x*ScreenWidth)-0.5f, (m_Quad[2].y*ScreenHeight)-0.5f, 0.0f, 0.5f, m_Color[2], m_UV2.x, m_UV2.y);
	VArray[5]=VArray[0];

	//setting the texture
	pG->SetTexture(0, m_pImage);

	//preparing additive
	if (m_IsAdditive)
	{
		GetGraphics()->SetupAdditiveAlpha(true);
	}

	//preparing mipmapping
	if (m_IsMipMapped)
	{	
		pG->SetTexStage(0, Graphics::MIP_FILTER, Graphics::TEX_LINEAR);
	}

	//processing quick alpha
	pG->SetRenderState(Graphics::RS_ALPHA_TEST, m_QuickAlpha); 
	
	//Rendering!
	pG->DrawVertex2D(VArray, 6);

	//cleaning up after additive
	if (m_IsAdditive)
	{
		GetGraphics()->SetupAdditiveAlpha(false);
	}

	//cleaning up after mipmapping
	if (m_IsMipMapped)
	{	
		pG->SetTexStage(0, Graphics::MIP_FILTER, Graphics::TEX_NONE);
	}

	return true;

}//bool TrSprite::Render()




bool TrSprite::Render_PerspCorByHeight(float MaxHeight)
{

	if (m_IsVisible == false || IsValid() == false)
		return false;

	Graphics* pG = GetGraphics();

	float	ScreenWidth = pG->GetScreenWidth();
	float	ScreenHeight = pG->GetScreenHeight();

	Graphics::Vertex2D*		VArray = pG->GetTempVert();

	//calculating the Ws
	float LeftW = (m_Quad[3].y - m_Quad[0].y) / MaxHeight;
	float RightW = (m_Quad[2].y - m_Quad[1].y) / MaxHeight;


	//filling the vertices array
	VArray[0].Fill((m_Quad[3].x*ScreenWidth)-0.5f, (m_Quad[3].y*ScreenHeight)-0.5f,0.0f,LeftW, m_Color[3], m_UV1.x, m_UV2.y);
	VArray[1].Fill((m_Quad[0].x*ScreenWidth)-0.5f, (m_Quad[0].y*ScreenHeight)-0.5f,0.0f,LeftW, m_Color[0], m_UV1.x, m_UV1.y);
	VArray[2].Fill((m_Quad[1].x*ScreenWidth)-0.5f, (m_Quad[1].y*ScreenHeight)-0.5f,0.0f,RightW, m_Color[1], m_UV2.x, m_UV1.y);
	
	VArray[3]=VArray[2];
	VArray[4].Fill((m_Quad[2].x*ScreenWidth)-0.5f, (m_Quad[2].y*ScreenHeight)-0.5f,0.0f,RightW, m_Color[2], m_UV2.x, m_UV2.y);
	VArray[5]=VArray[0];

	//setting the texture
	pG->SetTexture(0, m_pImage);

	//preparing additive
	if (m_IsAdditive)
	{
		GetGraphics()->SetupAdditiveAlpha(true);
	}

	//preparing mipmapping
	if (m_IsMipMapped)
	{	
		pG->SetTexStage(0, Graphics::MIP_FILTER, Graphics::TEX_LINEAR);
	}


	//processing quick alpha
	pG->SetRenderState(Graphics::RS_ALPHA_TEST, m_QuickAlpha); 
	
	//Rendering!
	pG->DrawVertex2D(VArray, 6);

	//cleaning up after additive
	if (m_IsAdditive)
	{
		GetGraphics()->SetupAdditiveAlpha(false);
	}

	//cleaning up after mipmapping
	if (m_IsMipMapped)
	{	
		pG->SetTexStage(0, Graphics::MIP_FILTER, Graphics::TEX_NONE);
	}

	return true;

}//bool TrSprite::Render_PerspCorByHeight(float MaxHeight)






bool TrSprite::Tick(float dt)
{
	if (m_Paused)
		dt = 0.0f;

	if (m_pSpriteInstance)
	{
		m_pSpriteInstance->GetUV(m_Timer, m_UV1, m_UV2);
		if (!m_pSpriteInstance->UpdateTime(m_LoopCount, m_Timer, dt, m_Looped))
		{
			return false;
		} 
		return true;
	} 
	return false;

}//bool TrSprite::Tick(float dt)







//TrSprite: Helper Routines
//======================================================================================================================

void TrSprite::SetupSprite(const std::string& ImageFile, bool IsAdditive, bool IsMipMapped, bool IsVisible, bool QuickAlpha, COLOR Color,
						   const math::Vec2& UV1, const math::Vec2& UV2)
{
	//setting the image
	SetImage(ImageFile);

	//setting additive state
	m_IsAdditive = IsAdditive;

	//setting mipmapping state
	m_IsMipMapped = IsMipMapped; 

	//setting visibility state
	m_IsVisible = IsVisible;

	//setting quick alpha state
	m_QuickAlpha = QuickAlpha;

	//setting color
	int i;

	for (i = 0; i < 4; i++)
		m_Color[i] = Color;

	//setting texture coords
	m_UV1 = UV1;
	m_UV2 = UV2;

}//void TrSprite::SetupSprite(...)





//TrSprite: Quad Routines
//======================================================================================================================

void TrSprite::Quad_Setup(const math::Vec2& TopLeft, const math::Vec2& Size)
{
	m_Quad[0] = TopLeft;
	
	m_Quad[1].x = TopLeft.x + Size.x;
	m_Quad[1].y = TopLeft.y;

	m_Quad[2].x = m_Quad[1].x;
	m_Quad[2].y = TopLeft.y + Size.y;

	m_Quad[3].x = TopLeft.x;
	m_Quad[3].y = m_Quad[2].y;

}



void TrSprite::Quad_Setup(const math::Vec2& P0, const math::Vec2& P1, const math::Vec2& P2, const math::Vec2& P3)
{
	m_Quad[0] = P0;
	
	m_Quad[1] = P1;

	m_Quad[2] = P2;

	m_Quad[3] = P3;

}



void TrSprite::Quad_SetupCentered(const math::Vec2& Center, float Radius)
{

	m_Quad[0].x = Center.x - Radius;
	m_Quad[0].y = Center.y - Radius;

	m_Quad[1].x = Center.x + Radius;
	m_Quad[1].y = Center.y - Radius;

	m_Quad[2].x = Center.x + Radius;
	m_Quad[2].y = Center.y + Radius;

	m_Quad[3].x = Center.x - Radius;
	m_Quad[3].y = Center.y + Radius;

}//void TrSprite::Quad_SetupCentered(math::Vec2 Center, float Radius)



void TrSprite::Quad_SetupCentered(const math::Vec2& Center, const math::Vec2& HorAndVerRadiuses)
{

	m_Quad[0].x = Center.x - HorAndVerRadiuses.x;
	m_Quad[0].y = Center.y - HorAndVerRadiuses.y;

	m_Quad[1].x = Center.x + HorAndVerRadiuses.x;
	m_Quad[1].y = Center.y - HorAndVerRadiuses.y;

	m_Quad[2].x = Center.x + HorAndVerRadiuses.x;
	m_Quad[2].y = Center.y + HorAndVerRadiuses.y;

	m_Quad[3].x = Center.x - HorAndVerRadiuses.x;
	m_Quad[3].y = Center.y + HorAndVerRadiuses.y;

}



void TrSprite::Quad_SetupTLBR(const math::Vec2& TopLeft, const math::Vec2& BottomRight)
{

	m_Quad[0] = TopLeft;

	m_Quad[1].x = BottomRight.x;
	m_Quad[1].y = TopLeft.y;

	m_Quad[2] = BottomRight;

	m_Quad[3].x = TopLeft.x;
	m_Quad[3].y = BottomRight.y;

}




void TrSprite::Quad_SetupFromImageTL(const math::Vec2& TopLeft)
{

	Quad_Setup(TopLeft, m_ImageSize); 

}//void TrSprite::Quad_SetupFromImageTL(const math::Vec2& TopLeft)



void TrSprite::Quad_SetupFromImageCentered(const math::Vec2& Center)
{

	Quad_SetupCentered(Center, m_ImageSize); 

}//void TrSprite::Quad_SetupFromImageCentered(const math::Vec2& Center)





void TrSprite::Quad_Move(const math::Vec2& Shift)
{
	int i;

	for (i = 0; i < 4; i++)
		m_Quad[i] += Shift;

}



void TrSprite::Quad_MoveX(float Shift)
{
	int i;

	for (i = 0; i < 4; i++)
		m_Quad[i].x += Shift;

}//void TrSprite::Quad_MoveX(float Shift)



void TrSprite::Quad_MoveY(float Shift)
{
	int i;

	for (i = 0; i < 4; i++)
		m_Quad[i].y += Shift;

}//void TrSprite::Quad_MoveY(float Shift)



void TrSprite::Quad_Rotate(const math::Vec2& Center, float Degrees)
{
	math::Vec2 ConvertedCenter = Center;
	ConvertedCenter.y *= GraphicsUtil::H2W; 

	//degrees to radians
	Degrees *= DEG_TO_RAD;

	//rotation
	int i;

	float X;
	float Y;
	float Sin = sinf(Degrees);
	float Cos = cosf(Degrees);

	for (i = 0; i < 4; i++)
	{
		//converting current quad point y-units to x-units
		m_Quad[i].y *= GraphicsUtil::H2W;

		//performing calculations
		X = m_Quad[i].x - ConvertedCenter.x;
		Y = m_Quad[i].y - ConvertedCenter.y;

		m_Quad[i].x = ConvertedCenter.x + ((X*Cos)-(Y*Sin));
		m_Quad[i].y = ConvertedCenter.y + ((X*Sin)+(Y*Cos));

		//restoring y-units for current quad point
		m_Quad[i].y *= GraphicsUtil::W2H;
	}

}



void TrSprite::Quad_Scale(const math::Vec2& Center, float ScaleFactor)
{

	math::Vec2 ConvertedCenter = Center;
	ConvertedCenter.y *= GraphicsUtil::H2W; 

	int i;

	for (i = 0; i < 4; i++)
	{
		//converting current quad point y-units to x-units
		m_Quad[i].y *= GraphicsUtil::H2W;

		//performing calculations
		m_Quad[i].x = ConvertedCenter.x + ((m_Quad[i].x - ConvertedCenter.x) * ScaleFactor);
		m_Quad[i].y = ConvertedCenter.y + ((m_Quad[i].y - ConvertedCenter.y) * ScaleFactor);

		//restoring y-units for current quad point
		m_Quad[i].y *= GraphicsUtil::W2H;
	}

}//void TrSprite::Quad_Scale(math::Vec2 Center, float ScaleFactor)



math::Vec2 TrSprite::Quad_GetPoint(int I)
{
	assert(I > -1 && I < 4);

	return m_Quad[I];

}//math::Vec2 TrSprite::Quad_GetPoint(int I)




//TrSprite: Color Routines
//======================================================================================================================

void TrSprite::SetColor(COLOR Color)
{
	for (int i = 0; i < 4; i++)
		m_Color[i] = Color;

}//void TrSprite::SetColor(COLOR Color)



void TrSprite::SetColor(COLOR C0, COLOR C1, COLOR C2, COLOR C3)
{
	m_Color[0] = C0;
	m_Color[1] = C1;
	m_Color[2] = C2;
	m_Color[3] = C3;

}//void TrSprite::SetColor(COLOR C0, COLOR C1, COLOR C2, COLOR C3)



COLOR TrSprite::GetColor(int I)
{
	assert(I > -1 && I < 4);

	return m_Color[I];

}//COLOR TrSprite::GetColor(int I)






//TrSprite: Texture Coords Routines
//======================================================================================================================

void TrSprite::SetUV(const math::Vec2& UV1, const math::Vec2& UV2)
{
	m_UV1 = UV1;
	m_UV2 = UV2;

}



void TrSprite::SetUV1(const math::Vec2& UV1)
{
	m_UV1 = UV1;

}



void TrSprite::SetUV2(const math::Vec2& UV2)
{
	m_UV2 = UV2;

}






//TrSprite: Image Routines
//======================================================================================================================

void TrSprite::SetImage(const std::string& ImageFile)
{

	if (IsValid()) Invalidate();

	if (ImageFile.empty())	
	{
		m_Filename.resize(0);
		return ;
	}

	std::string	Image = ImageFile;
	int	Locale = ImageFile.find("###");
	if (Locale >=0)
	{
		Image = ImageFile.substr(0, Locale) + 
				ui::UIManager::Instance().GetLocalization() + 
				ImageFile.substr(Locale+3, ImageFile.size()-(Locale+3));

		m_IsLocalized = true;
	}
	
	ui::SpriteInstance*	pSI = ui::SpriteSet::Instance().GetSprite(Image);
	if (pSI)
	{
		pSI->Init();		// force a load 
		m_pImage = NULL;	    // we're using the sprite instance instead
		m_pSpriteInstance = pSI;
		m_Timer = 0.0f;
		m_LoopCount = 0;
		m_pImage = m_pSpriteInstance->pImage;
		m_pSpriteInstance->GetFrameSize(m_ImageSize); 
	} else
	{
		m_pSpriteInstance = NULL;
		m_pImage = GetGraphics()->LoadImageFromFile(Image);
		if (!m_pImage)
		{
			_LOG(MSG_ERROR, "Unable to load image " << Image << " into the TrSprite");
			return ;
		}
		m_ImageSize.x = float(m_pImage->GetWidth()) / (float)GraphicsUtil::WIDTH;
		m_ImageSize.y = float(m_pImage->GetHeight())/ (float)GraphicsUtil::HEIGHT;
	}

	m_Filename = ImageFile;
	return;

}//void TrSprite::SetImage(const std::string& ImageFile)




void TrSprite::SetImage(const char* ImageData, int DataSize)
{
	if (IsValid()) Invalidate();

	assert(ImageData != NULL);
	assert(DataSize > 0);

	m_pImage = GetGraphics()->LoadImageFromMemory((unsigned char*)ImageData, DataSize); 

	assert(m_pImage != NULL);

	m_pSpriteInstance = NULL;

}//void TrSprite::SetImage(const char* ImageData, int DataSize)






//TrSprite: Sprite Instance Related Routines
//======================================================================================================================

float TrSprite::GetTotalAnimTime() const
{
	return m_pSpriteInstance ? m_pSpriteInstance->GetAnimTime() : 1.0f;

}//float TrSprite::GetTotalAnimTime() const






//TrSprite: Localization Routines
//======================================================================================================================

void TrSprite::UpdateLocalization()
{
	if (m_IsLocalized)
	{
		SetImage(m_Filename);
	}

}//void TrSprite::UpdateLocalization()




//TrSprite: Invalidation Routines
//======================================================================================================================

void TrSprite::Invalidate()
{

	if (m_pSpriteInstance)
	{
		// this is not our sprite to deal with
		m_pSpriteInstance = NULL;
		m_pImage = NULL;
	} else
	{
		UTIL_SAFE_DELETE(m_pImage);		
	}

}//void TrSprite::Invalidate()








